#include "Solver.h"

using namespace std;

Solver :: Solver(Configurator* configurator)
{
	dwarfConfigurator = configurator;					// process settings from the command line 
	dwarfConfigurator -> WriteSettings();
	dwarfConfigurator -> GetContent(&mainContent, &contentSize, &patternContent, &stateDimensionCoincidence);
}

void Solver::InitParallelization(char *contentChar, int contentSize)
{
	int interval = 1 + contentSize / (dwarfConfigurator -> GetThreadCount());   // partitioning the String content (same parts for all threads)
    int leftBoard = 0;
    int part = 0;
	threadBounds[0] = 0;
	while (part < dwarfConfigurator -> GetThreadCount() - 1)
    {
		int certainInterval = interval;
		int j = 0;
		while (j==0)
		{
			for (int i = 0; i < stateDimensionCoincidence; i++)
			{
				if (contentChar[leftBoard + certainInterval] == patternContent[i])
				{
					certainInterval++;
					break;
				}
				j++;
			}			
		}
		certainInterval++;
        part++;
        leftBoard += certainInterval;
        threadBounds[part] = leftBoard;
     }
     threadBounds[dwarfConfigurator -> GetThreadCount()] = contentSize;
}

void Solver::ConstructStates()
{
	states = (int**) malloc (sizeof(int*)*stateDimensionChars);				// new states array
	for (int i = 0; i < stateDimensionChars; i++)
	{
		states[i] = (int*) malloc (sizeof(int)*(stateDimensionCoincidence + 1));
	}

	for (int i = 0; i < stateDimensionCoincidence + 1; i++)
	{
		for (int j = 0; j < stateDimensionChars; j++)
		{
			states[j] [i] = 0;
		}
    }
    for (int i = 0; i < stateDimensionCoincidence; i++)
	{
		states[patternContent[0]] [i] = 1;				// all turns from any char to the the first char in pattern are "initial" (=1)
    }
	for (int i = 1; i < stateDimensionCoincidence; i++)
    {
		int currentState = states[patternContent[i]] [i];
        if (currentState > 0)							// cycle-in-pattern checker
        {
			states[patternContent[currentState]] [i + 1] = currentState + 1;    // if cycle found, add "cycle" state
        }
 		states[patternContent[i]] [i] = i + 1;			// all turns from i-char to the (i+1)-char are "next" (equals to i+1)
    }     
	free(patternContent);								// pattern won't be used later
}

void Solver::StatesProcessing(int beginPoint, int endPoint)
{
	int state = 0;
	for (int i = beginPoint; i < endPoint; i++)
	{
		if (mainContent[i] > 0)
		{
			state = states[mainContent[i]][state];			// change the state
		}
		if (state == stateDimensionCoincidence)				// pattern found!
		{
			omp_set_lock( &lk );
			positions.push_back(i);							// push to list
			omp_unset_lock( &lk );
			state = 0;
		}
	}
}

void Solver::Solve()
{
	ConstructStates();													// construct states array

	threadBounds = (int*) malloc (sizeof(int)*(dwarfConfigurator -> GetThreadCount() + 1));
	InitParallelization(mainContent, contentSize);
	omp_init_lock( &lk );
	#pragma omp parallel for	
		for (int i = 0; i < dwarfConfigurator -> GetThreadCount(); i++)
		{			
			StatesProcessing(threadBounds[i], threadBounds[i + 1]);		// traverse the part of string
		}
	omp_destroy_lock( &lk );
	positions.sort();
	free(mainContent);
}

void Solver::Finish(double time)		// Problem results output
{
	if (! positions.empty())
	{
		printf("\n\nTotal count of pattern entries: %d \n", positions.size());
	}
	else
	{
		printf("\n\nPattern not found        \n");
	}
	dwarfConfigurator -> Close(time, positions);
}
